﻿using Org.BouncyCastle.Crypto.Digests;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace JustMathCore.AddrToolCoin
{
    class ETH : AddrTools.IAddrConv
    {
        public AddrTools.AddrType Type
        {
            get
            {
                return AddrTools.AddrType.ETH;
            }
        }
        public byte[] Addr2Hash(string addr)
        {
            int start = addr.Length == 44 ? 4 : 2;
            byte[] hash = new byte[20];
            for (var i = 0; i < 20; i++)
            {
                hash[i] = byte.Parse(addr.Substring(start + i * 2, 2), NumberStyles.HexNumber);
            }
            return hash;
        }

        //public void Addr2Hash(string addr, byte[] outdata, int outpos)
        //{
        //    for (var i = 0; i < 20; i++)
        //    {
        //        outdata[i + outpos] = byte.Parse(addr.Substring(2 + i * 2, 2), NumberStyles.HexNumber);
        //    }
        //}

        public string GetAddr(byte[] prikey)
        {
            var addr = GetAddrHex(prikey);
            return GetFinalAddr(addr);
        }

        public byte[] GetHashAddr(byte[] prikey)
        {
            //性能接近了
            if (curve == null)
                curve = Org.BouncyCastle.Crypto.EC.CustomNamedCurves.GetByName("secp256k1");
            var g = curve.G;
            var k = new Org.BouncyCastle.Math.BigInteger(1, prikey);
            var pubkey = g.Multiply(k).GetEncoded(false);
            if (pubkey.Length == 1)
                return null;
            var array = CalculateHash3K(pubkey, 1, 64);
            var hash = new byte[20];

            for (var i = 12; i < 32; i++)
            {
                hash[i - 12] = array[i];
            }

            return hash;
        }

        public string GetWif(byte[] prikey)
        {
            Nethereum.Signer.EthECKey key = new Nethereum.Signer.EthECKey(prikey, true);
            var priaddr = key.GetPrivateKey();
            return priaddr;
        }

        public string Hash2Addr(byte[] hash)
        {
            var addr = "";
            for (var i = 0; i < 20; i++)
            {
                addr += hash[i].ToString("x2");
            }
            return GetFinalAddr(addr);
        }

        [ThreadStatic]
        static Org.BouncyCastle.Asn1.X9.X9ECParameters curve;
        public static string GetAddrHex(byte[] prikey)
        {
            //性能接近了
            if (curve == null)
                curve = Org.BouncyCastle.Crypto.EC.CustomNamedCurves.GetByName("secp256k1");
            var g = curve.G;
            var k = new Org.BouncyCastle.Math.BigInteger(1, prikey);
            var pubkey = g.Multiply(k).GetEncoded(false);
            if (pubkey.Length == 1)
                return "0x00";
            var array = CalculateHash3K(pubkey, 1, 64);
            var addr = "";
            for (var i = 12; i < 32; i++)
            {
                addr += array[i].ToString("x2");
            }
            return addr;
            //ffad79205cf194d541a07c3613a9c545bb1bda73

            //"0xFfAd79205cF194D541a07c3613A9C545bb1BDa73"

        }
        public static string GetFinalAddr(string hexaddr)
        {
            //最后这一段就是调整了一下大小写
            var text = CalculateHash3KStr(hexaddr);
            string text2 = "0x";
            for (int i = 0; i < hexaddr.Length; i++)
            {
                text2 = ((int.Parse(text[i].ToString(), NumberStyles.HexNumber) <= 7) ? (text2 + hexaddr[i]) : (text2 + hexaddr[i].ToString().ToUpper()));
            }
            return text2;
        }
        [ThreadStatic]
        static Nethereum.Util.Sha3Keccack sha3k = null;
        [ThreadStatic]
        static KeccakDigest keccakDigest = null;
        [ThreadStatic]
        static byte[] hashout;
        public static byte[] CalculateHash3K(byte[] value, int begin, int count)
        {
            if (sha3k == null)
            {
                sha3k = new Nethereum.Util.Sha3Keccack();
                keccakDigest = new KeccakDigest(256);
                hashout = new byte[keccakDigest.GetDigestSize()];
            }
            keccakDigest.Reset();
            keccakDigest.BlockUpdate(value, begin, count);
            keccakDigest.DoFinal(hashout, 0);
            return hashout;
        }
        public static string CalculateHash3KStr(string txt)
        {
            if (sha3k == null)
            {
                sha3k = new Nethereum.Util.Sha3Keccack();
                keccakDigest = new KeccakDigest(256);
                hashout = new byte[keccakDigest.GetDigestSize()];
            }
            var bytes = System.Text.Encoding.UTF8.GetBytes(txt);

            keccakDigest.Reset();
            if (hashout == null)
                hashout = new byte[keccakDigest.GetDigestSize()];
            keccakDigest.BlockUpdate(bytes, 0, bytes.Length);
            keccakDigest.DoFinal(hashout, 0);
            var str = "";
            for (var i = 0; i < hashout.Length; i++)
                str += hashout[i].ToString("x2");
            return str;
        }
    
    }
}
